// Copyright (c) 2023 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::structure::*;
use core::cell::RefCell;
use std::rc::Rc;

/// `Attribute` structure, which corresponds to `Interface Attr`.
///
/// # Association
/// [Interface Attr](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-637646024)
///
/// # Example
/// ```
/// use ylong_xml::{Document, DocumentMethod};
///
/// let document = Document::new();
/// let attribute = document.create_attribute("Attribute", "Attribute's Value").unwrap();
/// ```
#[derive(Debug, Clone)]
pub struct Attribute {
    pub(crate) inner: Rc<RefCell<AttributeInner>>,
}

impl Attribute {
    /// Create a default `Attribute` with the given name, value and its owner document.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Attribute, Document, NodeMethod, ValuedNodeMethod};
    ///
    /// let document = Document::new();
    /// let attribute = Attribute::new("Attribute", "Attribute's Value", &document).unwrap();
    /// assert_eq!(attribute.name(), String::from("Attribute"));
    /// assert_eq!(attribute.value(), String::from("Attribute's Value"));
    /// ```
    pub fn new(name: &str, val: &str, owner_document: &Document) -> Result<Self, XmlError> {
        Ok(Self {
            inner: Rc::new(RefCell::new(AttributeInner::new(
                name.to_string(),
                val.to_string(),
                Some(owner_document.downgrade()),
            ))),
        })
    }

    /// Create an `Attribute` with child members.
    pub(crate) fn new_unchecked(
        name: String,
        val: String,
        owner_document: Option<WeakDocument>,
    ) -> Self {
        Self {
            inner: Rc::new(RefCell::new(AttributeInner::new(name, val, owner_document))),
        }
    }
}

impl NodeMethod for Attribute {
    fn as_node(&self) -> Node {
        Node::Attribute(self.clone())
    }

    fn name(&self) -> String {
        self.inner.borrow().name.clone()
    }

    fn node_type(&self) -> NodeType {
        NodeType::Attribute
    }

    fn owner_document(&self) -> Option<Document> {
        self.inner.borrow().owner_document.clone()?.upgrade()
    }
}

impl ValuedNodeMethod for Attribute {
    fn value(&self) -> String {
        self.inner.borrow().val.clone()
    }

    fn set_value(&mut self, value: &str) {
        self.inner.borrow_mut().val = value.to_string();
    }
}

impl PartialEq for Attribute {
    fn eq(&self, other: &Self) -> bool {
        self.inner.as_ptr() == other.inner.as_ptr()
    }
}

#[derive(Debug)]
pub(crate) struct AttributeInner {
    pub(crate) name: String,
    pub(crate) val: String,
    pub(crate) owner_document: Option<WeakDocument>,
}

impl AttributeInner {
    pub(crate) fn new(name: String, val: String, owner_document: Option<WeakDocument>) -> Self {
        Self {
            name,
            val,
            owner_document,
        }
    }
}

#[cfg(test)]
mod ut_attribute {
    use crate::structure::*;
    /*
     * @title  UT test for attribute_inner debug derive
     * @design Condition Coverage
     * @precon NA
     * @brief  1. Call `new()` method to create a `Attribute`.
     *         2. Check if the attribute name is \"Attribute\"。
     * @expect 1. true。
     * @auto   yes
     */
    #[test]
    fn ut_attribute_inner_new() {
        let document = Document::new();
        let weak_document = document.downgrade();
        let optional_doc = Some(weak_document);
        let attribute = attribute::AttributeInner::new(
            String::from("Attribute"),
            String::from("Value"),
            optional_doc,
        );
        assert_eq!(format!("{:?}", &attribute.name), "\"Attribute\"");
    }
    /// UT test case for `Attribute::new()`.
    ///
    /// # Brief
    /// 1. Call `new()` method to create a `Attribute`.
    /// 2. Check if parts of the `Attribute` are default values.
    #[test]
    fn ut_attribute_new() {
        let document = Document::new();
        let attribute = Attribute::new("Attribute", "Value", &document).unwrap();
        let attribute_inner = attribute.inner.borrow();
        assert_eq!(attribute_inner.name, "Attribute");
        assert_eq!(attribute_inner.val, "Value");
        assert_eq!(attribute_inner.owner_document, Some(document.downgrade()));
    }

    /// UT test case for `Attribute::as_node()`.
    ///
    /// # Brief
    /// 1. Create a `Attribute` called `attribute`.
    /// 2. Call `as_node()` method to convert `attribute` to `Node`.
    /// 3. Check if `Node::Attribute(attribute)` is obtained.
    #[test]
    fn ut_attribute_as_node() {
        let document = Document::new();
        let attribute = document.create_attribute("Attribute", "Value").unwrap();
        let node = attribute.as_node();
        assert_eq!(node, Node::Attribute(attribute));
    }

    /// UT test case for `Attribute::name()`.
    ///
    /// # Brief
    /// 1. Create a `Attribute` called `attribute`.
    /// 2. Call `name()` method to obtain `attribute`'s name.
    /// 3. Check if the obtained name is the given name.
    #[test]
    fn ut_attribute_name() {
        let document = Document::new();
        let attribute = document.create_attribute("Attribute", "Value").unwrap();
        assert_eq!(attribute.name(), "Attribute");
    }

    /// UT test case for `Attribute::node_type()`.
    ///
    /// # Brief
    /// 1. Create a `Attribute` called `attribute`.
    /// 2. Call `node_type()` method to obtain `attribute`'s type.
    /// 3. Check if this obtained node type is `NodeType::Attribute`.
    #[test]
    fn ut_attribute_node_type() {
        let document = Document::new();
        let attribute = document.create_attribute("Attribute", "Value").unwrap();
        assert_eq!(attribute.node_type(), NodeType::Attribute);
    }

    /// UT test case for `Attribute::owner_document()`.
    ///
    /// # Brief
    /// 1. Create a `Document` called `document`.
    /// 2. Create an `Attribute` of `document`, which is called `attribute`.
    /// 3. Call `owner_document()` method to obtain `attribute`'s owner document.
    /// 4. Check if `attribute`'s owner document is `Some(document)`.
    #[test]
    fn ut_attribute_owner_document() {
        let document = Document::new();
        let attribute = document.create_attribute("Attribute", "Value").unwrap();
        assert_eq!(attribute.owner_document(), Some(document));
    }

    /// UT test case for `Attribute::value()`.
    ///
    /// # Brief
    /// 1. Create a `Attribute` called `attribute` whose value is set to `"Value"`.
    /// 2. Call `value()` method to obtain `attribute`'s value.
    /// 3. Check if the value is equal to `"Value"`.
    #[test]
    fn ut_attribute_value() {
        let document = Document::new();
        let attribute = document.create_attribute("Attribute", "Value").unwrap();
        assert_eq!(attribute.value(), "Value");
    }

    /// UT test case for `Attribute::set_value()`.
    ///
    /// # Brief
    /// 1. Create a `Attribute` called `attribute` whose value is set to `"OldValue"`.
    /// 2. Call `set_value()` method to set the value of `attribute` to `"NewValue"`.
    /// 3. Get the value of `attribute` and check if it is equal to `"NewValue"`.
    #[test]
    fn ut_attribute_set_value() {
        let document = Document::new();
        let mut attribute = document.create_attribute("Attribute", "OldValue").unwrap();
        attribute.set_value("NewValue");
        assert_eq!(attribute.value(), "NewValue");
    }
}
